import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/* steam流式各种操作学习笔记 */
public class StreamTest {

    List<Student> students = Arrays.asList(
            new Student("Alice", 20, "A", new BigDecimal("95.5"), "CS"),
            new Student("Bob", 21, "B", new BigDecimal("87.2"), "Math"),
            new Student("Charlie", 19, "A", new BigDecimal("92.0"), "CS"),
            new Student("David", 22, "C", new BigDecimal("78.5"), "Physics"),
            new Student("Eve", 20, "B", new BigDecimal("89.8"), "Math"),
            new Student("Frank", 21, "A", new BigDecimal("94.3"), "CS"),
            new Student(null, 23, "C", new BigDecimal("82.1"), null) // 测试null值
    );

    // 一.List<Object> 转List<基本数据类型>
    List<String> studentNos = students.stream().map(Student::getGrade).collect(Collectors.toList());

    // 方法1：使用Collectors.toSet()去重（推荐）
    Set<String> studentNosSet = students.stream()
            .map(Student::getGrade)
            .collect(Collectors.toSet());

    // 方法2：使用distinct()去重并转为List
    List<String> studentNosList = students.stream()
            .map(Student::getGrade)
            .distinct()
            .collect(Collectors.toList());

    // 方法3：如果需要保持插入顺序且转为List
    List<String> orderedUniqueList = students.stream()
            .map(Student::getGrade)
            .distinct()
            .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);


    // 二.List<Object> 转Map
    // 1.需要注意这个方式，如果Key一样是会报错的
    Map<Long, String> stringMap2 = students.stream().collect(Collectors.toMap(Student::getId, Student::getName));
     // 2.如果Key重复了，需要指定一个合并策略
     Map<Long, String> stringMap3 = students.stream().collect(Collectors.toMap(Student::getId, Student::getName, (oldValue, newValue) -> oldValue));
     // 3 如果希望Map的value是一个对象可采用分组的写法
     Map<Long, List<Student>> stuMap = students.stream().collect(Collectors.groupingBy(Student::getId));

     // 三.List<Object> 转字符串
     // 1.将字符串数组转为逗号分隔的字符串
     String str = students.stream().map(Student::getName).collect(Collectors.joining(","));
     //Arrays.stream(orderGood.getSpecifications()).collect(Collectors.joining(","));

     // 2.将List<Object> 转为Set<基本数据类型> 将一个集合中的某个 逗号分隔的字符串属性转为Set
     Set<String> userIds = unFinishApproveDetails.stream()
                .map(BaseApproveDetail::getNodeUserIds)                    // 提取 tags 字符串
                .filter(Objects::nonNull)                           // 过滤 null 值
                .filter(s -> !s.trim().isEmpty())                   // 过滤空或空白字符串
                .flatMap(s -> Arrays.stream(s.split(",")))          // 拆分并扁平化为单个字符串流
                .map(String::trim)                                  // 去除每个字符串前后空格（可选）
                .collect(Collectors.toSet());

    /**
     * 基础单字段分组
     */
    public static void basicGrouping(List<Student> students) {
        // 按年级分组（推荐写法）
        Map<String, List<Student>> groupedByGrade = students.stream()
                .filter(Objects::nonNull) // 过滤null元素
                .collect(Collectors.groupingBy(Student::getGrade));

        System.out.println("按年级分组:");
        groupedByGrade.forEach((grade, list) ->
                System.out.println("  Grade " + grade + ": " + list.size() + " students"));

        // 按部门分组（处理null键）
        Map<String, List<Student>> groupedByDept = students.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(
                        s -> s.getDepartment() != null ? s.getDepartment() : "Unknown",
                        Collectors.toList()
                ));

        System.out.println("\n按部门分组（处理null）:");
        groupedByDept.forEach((dept, list) ->
                System.out.println("  Department " + dept + ": " + list.size() + " students"));
    }

    /**
     * 多级分组
     */
    public static void multiLevelGrouping(List<Student> students) {
        // 先按部门，再按年级分组
        Map<String, Map<String, List<Student>>> multiGrouped = students.stream()
                .filter(Objects::nonNull)
                .filter(s -> s.getDepartment() != null && s.getGrade() != null)
                .collect(Collectors.groupingBy(
                        Student::getDepartment,
                        Collectors.groupingBy(Student::getGrade)
                ));

        System.out.println("多级分组（部门->年级）:");
        multiGrouped.forEach((dept, gradeMap) -> {
            System.out.println("  Department: " + dept);
            gradeMap.forEach((grade, list) ->
                    System.out.println("    Grade " + grade + ": " + list.size() + " students"));
        });

        // 自定义分组键
        Map<String, List<Student>> customGrouped = students.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(s ->
                        s.getAge() < 21 ? "Young (<21)" : "Adult (>=21)"
                ));

        System.out.println("\n自定义年龄分组:");
        customGrouped.forEach((ageGroup, list) ->
                System.out.println("  " + ageGroup + ": " + list.size() + " students"));
    }

    /**
     * 分组聚合计算
     */
    public static void groupingWithAggregation(List<Student> students) {
        // 按年级分组并计算平均分
        Map<String, Double> avgScoreByGrade = students.stream()
                .filter(Objects::nonNull)
                .filter(s -> s.getScore() != null)
                .collect(Collectors.groupingBy(
                        Student::getGrade,
                        Collectors.averagingDouble(s -> s.getScore().doubleValue())
                ));

        System.out.println("按年级分组平均分:");
        avgScoreByGrade.forEach((grade, avg) ->
                System.out.printf("  Grade %s: %.2f%n", grade, avg));

        // 按部门分组并统计人数及最高分
        Map<String, Map<String, Object>> deptStats = students.stream()
                .filter(Objects::nonNull)
                .filter(s -> s.getDepartment() != null && s.getScore() != null)
                .collect(Collectors.groupingBy(
                        Student::getDepartment,
                        Collectors.collectingAndThen(
                                Collectors.toList(),
                                list -> {
                                    long count = list.size();
                                    Optional<Student> topStudent = list.stream()
                                            .max(Comparator.comparing(Student::getScore));

                                    Map<String, Object> stats = new HashMap<>();
                                    stats.put("count", count);
                                    stats.put("topStudent", topStudent.orElse(null));
                                    return stats;
                                }
                        )
                ));

        System.out.println("\n部门统计信息:");
        deptStats.forEach((dept, stats) -> {
            System.out.println("  Department: " + dept);
            System.out.println("    Count: " + stats.get("count"));
            Student top = (Student) stats.get("topStudent");
            if (top != null) {
                System.out.println("    Top Score: " + top.getName() + " (" + top.getScore() + ")");
            }
        });
    }

    /**
     * 特殊分组场景
     */
    public static void specialGroupingScenarios(List<Student> students) {
        // 使用LinkedHashMap保持插入顺序
        Map<String, List<Student>> orderedGrouping = students.stream()
                .filter(Objects::nonNull)
                .sorted(Comparator.comparing(Student::getGrade))
                .collect(Collectors.groupingBy(
                        Student::getGrade,
                        LinkedHashMap::new,  // 保持分组顺序
                        Collectors.toList()
                ));

        System.out.println("有序分组:");
        orderedGrouping.forEach((grade, list) ->
                System.out.println("  Grade " + grade + ": " + list.stream()
                        .map(Student::getName)
                        .collect(Collectors.joining(", "))));

        // 并行流分组（大数据量时使用）
        Map<String, List<Student>> parallelGrouping = students.parallelStream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingByConcurrent(Student::getGrade));

        System.out.println("\n并行流分组:");
        parallelGrouping.forEach((grade, list) ->
                System.out.println("  Grade " + grade + ": " + list.size() + " students"));

        // 分组到Set避免重复
        Map<String, Set<Student>> setGrouping = students.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(
                        Student::getGrade,
                        Collectors.toSet()  // 使用Set去重
                ));

        System.out.println("\n分组到Set:");
        setGrouping.forEach((grade, set) ->
                System.out.println("  Grade " + grade + ": " + set.size() + " unique students"));

        // 处理null值的安全分组
        Function<Student, String> safeGradeExtractor = s ->
                s.getGrade() != null ? s.getGrade() : "UNASSIGNED";

        Map<String, List<Student>> safeGrouping = students.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(safeGradeExtractor));

        System.out.println("\n安全分组（处理null键）:");
        safeGrouping.forEach((grade, list) ->
                System.out.println("  Grade " + grade + ": " + list.size() + " students"));
    }

    /**
     * 高效分组工具方法
     */
    public static <T, K> Map<K, List<T>> groupSafely(Collection<T> items,
                                                     Function<? super T, ? extends K> classifier) {
        return items.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.groupingBy(classifier, ConcurrentHashMap::new, Collectors.toList()));
    }

    /**
     * 多字段组合分组
     */
    public static void multiFieldGrouping(List<Student> students) {
        // 使用自定义key类进行多字段分组
        record GradeDeptKey(String grade, String department) {}

        Map<GradeDeptKey, List<Student>> multiFieldGroups = students.stream()
                .filter(Objects::nonNull)
                .filter(s -> s.getGrade() != null && s.getDepartment() != null)
                .collect(Collectors.groupingBy(
                        s -> new GradeDeptKey(s.getGrade(), s.getDepartment())
                ));

        System.out.println("多字段组合分组:");
        multiFieldGroups.forEach((key, list) ->
                System.out.println("  " + key.grade() + "-" + key.department() + ": " + list.size()));
    }

    /**
     * 通用方法：检查集合中是否存在任意元素的指定字段等于特定值（同时处理 null 和空字符串）
     * 注意：此方法更通用，不仅仅限于字符串
     *
     * @param collection 待检查的集合
     * @param getter     获取字段值的函数式接口
     * @param targetValue 目标值
     * @param <T>        集合元素的类型
     * @param <R>        字段值的类型
     * @return           如果存在则返回 true，否则 false
     */
    public static <T, R> boolean checkAnyHasSpecificValue(Collection<T> collection,
                                                          java.util.function.Function<T, R> getter,
                                                          R targetValue) {
        if (collection == null || collection.isEmpty()) {
            return false;
        }
        // 使用 Objects.equals 安全比较，能处理 targetValue 或 getter 结果为 null 的情况
        return collection.stream()
                .filter(Objects::nonNull)
                .map(getter)
                .anyMatch(val -> Objects.equals(val, targetValue));
    }

    /**
     * 使用传统的 Predicate 定义方式 (更显式，但略冗长)
     * 检查是否存在 name 字段有效
     */
    public static boolean checkAnyHasValidNameTraditional(List<Person> people) {
        if (people == null || people.isEmpty()) return false;

        Predicate<Person> hasNonNullName = p -> p.getName() != null;
        Predicate<Person> hasNonEmptyName = p -> !p.getName().isEmpty();

        return people.stream()
                .filter(Objects::nonNull)
                .filter(hasNonNullName)
                .anyMatch(hasNonEmptyName);
        // 或者合并成一行:
        // return people.stream().filter(Objects::nonNull).anyMatch(p -> p.getName() != null && !p.getName().isEmpty());
    }

}
